package abel.wlmr.model.type;

import abel.util.HexString;
import abel.wlmr.model.WlmrUserData;
import abel.wlmr.model.WlmrException;
import abel.wlmr.model.type.WlmrControlField.CtrlAcd;
import abel.wlmr.model.type.WlmrControlField.CtrlDir;
import io.netty.buffer.ByteBuf;

/**
 * B.4.3.4.6.1 附加信息域AUX定义
 * 附加信息域可由消息认证码字段 PW、事件计数器 EC 和时间标签 Tp 组成，格式见图 B.10。
 * 消息认证码字段 PW（下行） ⁄事件计数器 EC（上行）
 * 时间标签 Tp
 * 图B.10 附加信息域定义
 * ——消息认证码字段 PW 用于重要的下行报文中；
 * ——事件计数器 EC 用于具有重要事件告警状态需上报的上行报文中；
 * ——时间标签用于允许同时建立多个通信服务的链路传输和信道延时特性较差的传输中。
 * 
 * @author Abel
 *
 */
public class AuxBody extends WlmrUserData {
    private HexString pwd;

    private EventCountField ec;

    private TimeBody tp;

    private WlmrControlField.CtrlDir dir;

    private boolean requirePwd;

    private WlmrControlField.CtrlAcd acd;

    private boolean requireTpv;

    /**
     * 
     */
    public AuxBody() {
        super();
    }

    /**
     * @param guessedLen
     */
    public AuxBody(short guessedLen) {
        super(guessedLen);
    }

    @Override
    public ByteBuf appendBuf(ByteBuf buf) {
        if (requirePwd && CtrlDir.Down == dir) {
            if (pwd.length() != 16) {
                throw new IllegalArgumentException("pwd should be of length 16");
            }

            buf.writeBytes(pwd.getBs());
        } else if (CtrlDir.Up == dir && CtrlAcd.HasEvents == acd) {
            if (ec == null) {
                throw new IllegalArgumentException("ec should not be null if there're events");
            }

            buf.writeShortLE(ec.getValue());
        }

        if (requireTpv) {
            if (tp == null) {
                throw new IllegalArgumentException("tp should not be null if required");
            }

            tp.appendBuf(buf);
        }

        return buf;
    }

    @Override
    public int parseBuf(ByteBuf buf) throws WlmrException {
        if (buf.readableBytes() < getGuessedLength()) {
            return -1;
        }

        // verify
        {
            if (requirePwd && WlmrControlField.CtrlDir.Down.equals(dir)) {
                if (buf.readableBytes() < 16) {
                    return -1;
                }
            } else if (CtrlDir.Up.equals(dir) && CtrlAcd.HasEvents.equals(acd)) {
                if (buf.readableBytes() < 2) {
                    return -1;
                }
            }

            if (requireTpv && buf.readableBytes() < 6) {
                return -1;
            }
        }

        // parse really
        {
            if (requirePwd && CtrlDir.Down.equals(dir)) {
                pwd = new HexString(buf, 16);
            } else if (CtrlDir.Up.equals(dir) && CtrlAcd.HasEvents.equals(acd)) {
                ec = new EventCountField();
                ec.parseValue(buf.readShortLE());
            }

            if (requireTpv) {
                tp = new TimeBody();
                tp.parseBuf(buf);
            }
        }

        return getGuessedLength();
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.WlmrDataPart#getFinalLen()
     */
    @Override
    public short getFinalLen() {
        if (finalLen > 0) {
            return finalLen;
        }

        if (CtrlDir.Down.equals(dir)) {
            finalLen = (short) ((requirePwd ? 16 : 0) + (requireTpv ? 6 : 0));
        } else {
            finalLen = (short) ((CtrlAcd.HasEvents == acd ? 2 : 0)
                + (requireTpv ? 6 : 0));
        }

        return finalLen;
    }

    @Override
    public int getMinLength() {
        return 0;
    }

    /**
     * @return the pwd
     */
    public HexString getPwd() {
        return pwd;
    }

    /**
     * @param pwd
     *            the pwd to set
     */
    public void setPwd(HexString pwd) {
        this.pwd = pwd;
    }

    /**
     * @return the ec
     */
    public EventCountField getEc() {
        return ec;
    }

    /**
     * @param ec
     *            the ec to set
     */
    public void setEc(EventCountField ec) {
        this.ec = ec;
    }

    /**
     * @return the tp
     */
    public TimeBody getTp() {
        return tp;
    }

    /**
     * @param tp
     *            the tp to set
     */
    public void setTp(TimeBody tp) {
        this.tp = tp;
    }

    /**
     * @return the dir
     */
    public WlmrControlField.CtrlDir getDir() {
        return dir;
    }

    /**
     * @param dir
     *            the dir to set
     */
    public void setDir(WlmrControlField.CtrlDir dir) {
        this.dir = dir;
    }

    /**
     * @return the requirePwd
     */
    public boolean isRequirePwd() {
        return requirePwd;
    }

    /**
     * @param requirePwd
     *            the requirePwd to set
     */
    public void setRequirePwd(boolean requirePwd) {
        this.requirePwd = requirePwd;
    }

    /**
     * @return the acd
     */
    public WlmrControlField.CtrlAcd getAcd() {
        return acd;
    }

    /**
     * @param acd
     *            the acd to set
     */
    public void setAcd(WlmrControlField.CtrlAcd acd) {
        this.acd = acd;
    }

    /**
     * @return the requireTpv
     */
    public boolean isRequireTpv() {
        return requireTpv;
    }

    /**
     * @param requireTpv
     *            the requireTpv to set
     */
    public void setRequireTpv(boolean requireTpv) {
        this.requireTpv = requireTpv;
    }

}
